﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.IO;

namespace IcqSharp
{

    public enum SearchType : byte
    {
        UID = 0,
        EMail = 1,
        Names = 2,
        Details = 4
    }
    public enum CookieAckType : byte
    {
        None = 0,
        Server = 1,
        Client = 2
    }
    public enum RequestType : byte
    {
        Owner = 0,
        UserAuto = 1,
        UserMinimal = 2,
        UserDetailed = 3,
        Profile = 4
    }
    public enum DirectoryRequest : byte
    {
        InfoUser = 0x01,
        InfoOwner = 0x02,
        InfoMulti = 0x03,
        Search = 0x08,
        UpdateOwner = 0x10,
        UpdateNote = 0x11,
        UpdatePrivacy = 0x12
    }
    public enum MessageType : ushort
    {
        Unknown = 0x00, // Unknown message

        Plain = 0x01, // Plain text (simple) message
        Chat = 0x02, // Chat request message
        FileReq = 0x03, // File request / file ok message
        URL = 0x04, // URL message (0xFE formatted)
        AuthReq = 0x06, // Authorization request message (0xFE formatted)
        AuthDeny = 0x07, // Authorization denied message (0xFE formatted)
        AuthOK = 0x08, // Authorization given message (empty)
        Server = 0x09, // Message from OSCAR server (0xFE formatted)
        Added = 0x0C, // "You-were-added" message (0xFE formatted)
        WWP = 0x0D, // Web pager message (0xFE formatted)
        EExpress = 0x0E, // Email express message (0xFE formatted)
        Contacts = 0x13, // Contact list message
        Plugin = 0x1A, // Plugin message described by text string

        AutoOnline = 0xE7, // Auto online message (internal only)
        AutoAway = 0xE8, // Auto away message
        AutoBusy = 0xE9, // Auto occupied message
        AutoNA = 0xEA, // Auto not available message
        AutoDND = 0xEB, // Auto do not disturb message
        AutoFFC = 0xEC, // Auto free for chat message

        GreetingCard = 0x101, // Greeting Card
        RequestContacts = 0x102, // Request for Contacts
        Message = 0x103, // Message+
        StatusMsgExt = 0x104, // StatusMsgExt (2003b)
        SmsMessage = 0x110, // SMS message from Mobile

        ScriptInvitation = 0x201, // Xtraz Invitation
        ScriptData = 0x202, // Xtraz Message
        ScriptNotify = 0x208, // Xtraz Response

        ReverseRequest = 0x401 // Reverse DC request

    }

    // Basic structure used to hold operation cookies list
    public class CookieInfo
    {
        public UInt32 Cookie;
        public UInt32 ContactID;
        public Byte[] Extra;
        public DateTime Time;
        public Byte Type;
    }

    // Specific structures to hold request specific data - pvExtra
    public class CookieFamilyRequest
    {
        public UInt16 Family;
        public FamilyHandlerDelegate FamilyHandler;
        public delegate void FamilyHandlerDelegate(UInt32 Conn, String cookie);
    }
    public class CookieOfflineMessages
    {
        public Int32 Messages;
        public Int32 Missed;

        public static implicit operator Byte[](CookieOfflineMessages m)
        {
            var s = new MemoryStream(8);
            s.Write(m.Messages);
            s.Write(m.Missed);

            return s.ToArray();
        }

        
    }

    public class CookieMessageData
    {
        public UInt32 MsgID1;
        public UInt32 MsgID2;
        public UInt16 MessageType;
        public Byte AckType;

        public CookieMessageData() { }
        public CookieMessageData(Byte[] data)
        {
            if (data.Length != 10) Debugger.Break();

            MsgID1 = BitConverter.ToUInt32(data, 0);
            MsgID2 = BitConverter.ToUInt32(data, 4);
            MessageType = BitConverter.ToUInt16(data, 8);
            AckType = data[10];
        }

    }
    public class CookieMessageDataExt : CookieMessageData
    {
        public Boolean IsOffline;
    }

    public class CookieFam15Data
    {
        public Byte RequestType;
    }




    public class CookieSearch
    {
        public Byte SearchType;
        public String Object;
        public UInt32 MainId;
        public UInt32 Status;
    }
    public class CookieAvatar
    {
        public UInt32 Uin;
        public UInt32 Contact;
        public Byte[] hash;
        public UInt32 Data;
        public String File;
    }

    public class CookieReverseConnect : CookieMessageData
    {
        public UInt32 Contact;
        public UInt32 Uin;
        public Int32 Type;
        public dynamic ft;
    }



    public class CookieDirectoryData
    {
        public Byte RequestType;

        public static implicit operator Byte[](CookieDirectoryData d)
        {
            var s = new MemoryStream(1);
            s.Write(d.RequestType);
            return s.ToArray();
        }
    }


    public static class Cookies
    {
        public static List<CookieInfo> cookies 
                = new List<CookieInfo>();

        public static void RemoveExpiredCookies()
        {
            var now = DateTime.Now;

            for (int i = cookies.Count - 1; i >= 0; i--)
            {
                var cookie = cookies[i];
                if (cookie.Time.AddHours(1) < now)
                    cookies.Remove(cookie);
            }
        }


        // Generate and allocate cookie
        public static UInt32 AllocateCookie(CookieType Type, UInt16 Ident, UInt32 Contact, Byte[] Extra)
        {
            //icq_lock l(cookieMutex);

            var ThisSeq = (UInt32)Session.CookieSeq++;

            ThisSeq &= 0x7FFF;
            ThisSeq |= (UInt32)(Ident << 0x10);

            var c = new CookieInfo() {
                Type = (Byte)Type, Cookie = ThisSeq,
                ContactID = Contact, Extra = Extra,
                Time = DateTime.Now };

            cookies.Add(c);

            return ThisSeq;
        }


        public static UInt32 GenerateCookie(UInt16 Ident)
        {
            //icq_lock l(cookieMutex);

            UInt32 dwThisSeq = Session.CookieSeq++;
            dwThisSeq &= 0x7FFF;
            dwThisSeq |= (UInt32)Ident<<0x10;

            return dwThisSeq;
        }


        //Int32 GetCookieType(UInt32 Cookie)
        //{
        //    //icq_lock l(cookieMutex);

        //    int i = cookies.getIndex(( icq_cookie_info* )&dwCookie );
        //    if (i != INVALID_COOKIE_INDEX )
        //        i = cookies[i].Type;

        //    return i;
        //}


        public static CookieInfo FindCookie(UInt32 Cookie)
        {
            //icq_lock l(cookieMutex);

            return cookies.FirstOrDefault(c => c.Cookie == Cookie);



            //int i = cookies.getIndex(( icq_cookie_info* )&dwCookie );
            //if (i != INVALID_COOKIE_INDEX)
            //{
            //    if (phContact)
            //        *phContact = cookies[i]->hContact;
            //    if (ppvExtra)
            //        *ppvExtra = cookies[i]->pvExtra;

            //    // Cookie found
            //    return 1;
            //}

            //return 0;
        }
        public static CookieInfo FindCookie(Byte[] Extra)
        {
            return cookies.FirstOrDefault(c => c.Extra == Extra);
        }
        public static CookieInfo FindCookie(Byte Type)
        {
            return cookies.FirstOrDefault(c => c.Type == Type);
        }




        public static CookieInfo FindMessageCookie(UInt32 MsgID1, UInt32 MsgID2)
        {
            //icq_lock l(cookieMutex);

            for (int i = 0; i < cookies.Count; i++)
                if (cookies[i].Type == (Byte)CookieType.Message ||
                    cookies[i].Type == (Byte)CookieType.File || 
                    cookies[i].Type == (Byte)CookieType.ReverseDirect)
                { 
                    var data = new CookieMessageData(cookies[i].Extra);
                    if (data.MsgID1 == MsgID1 && 
                        data.MsgID2 == MsgID2)
                            return cookies[i];
                }

            return null;
        }


        public static void FreeCookie(UInt32 Cookie)
        {
            //icq_lock l(cookieMutex);

            var c = FindCookie(Cookie);
            if (c != null) cookies.Remove(c);

            RemoveExpiredCookies();
        }
        public static void FreeCookie(Byte[] Extra)
        {
            //icq_lock l(cookieMutex);

            var c = FindCookie(Extra);
            if (c != null) cookies.Remove(c);

            RemoveExpiredCookies();
        }


        public static void InitMessageCookie(CookieMessageData Cookie)
        {
            UInt32 MsgID1, MsgID2;

            // ensure that message ids are unique
            do
            { 
                MsgID1 = (UInt32)DateTime.Now.Ticks;
                MsgID2 = (UInt32)new Random((Int32)MsgID1).Next(0, 0x0FFFF); 
            } 
            while (FindMessageCookie(MsgID1, MsgID2) != null);

            if (Cookie != null)
            {
                Cookie.MsgID1 = MsgID1;
                Cookie.MsgID2 = MsgID2;
            }
        }


        public static CookieMessageData CreateMessageCookie(UInt16 MsgType, Byte AckType)
        {
            var cookie = (MsgType == (UInt16)MessageType.Plain) ?
                new CookieMessageDataExt() : 
                new CookieMessageData();

            cookie.MessageType = MsgType;
            cookie.AckType = AckType;

            InitMessageCookie(cookie);
            
            return cookie;
        }


        public static CookieMessageData CreateMessageCookieData(Byte MsgType, Contact Contact, UInt32 Uin, Boolean UseSrvRelay)
        {
            Byte AckType; 
            UInt16 Status = 0; //Contacts.Find( GetContactStatus(Contact);

            if (Session.SlowSend == 0) AckType = 0;
            else
                AckType = (Uin != 0 || Status != (UInt16)IcqStatus.Offline) ?
                    (Byte)CookieAckType.Server : (Byte)CookieAckType.None;

        /*
            if (!getSettingByte(hContact, "SlowSend", DEFAULT_SLOWSEND) || !getSettingByte(NULL, "SlowSend", DEFAULT_SLOWSEND) ||
                (!dwUin && wStatus == ID_STATUS_OFFLINE))
                bAckType = ACKTYPE_NONE;
            else if ((bUseSrvRelay && ((!dwUin) || (!CheckContactCapabilities(hContact, CAPF_SRV_RELAY)) ||
                (wStatus == ID_STATUS_OFFLINE))) || getSettingByte(hContact, "OnlyServerAcks", DEFAULT_ONLYSERVERACKS) ||
                getSettingByte(NULL, "OnlyServerAcks", DEFAULT_ONLYSERVERACKS))
                bAckType = ACKTYPE_SERVER;
            else
                bAckType = ACKTYPE_CLIENT;
        */

            return CreateMessageCookie(MsgType, AckType);
        }

    }
}
